package com.smartplatform.service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.smartplatform.cache.CategoriesCache;
import com.smartplatform.cache.SceneCache;
import com.smartplatform.constants.ResultCode;
import com.smartplatform.dto.DeviceDto;
import com.smartplatform.event.DeviceColorCommandEvent;
import com.smartplatform.event.DeviceLogEvent;
import com.smartplatform.event.DevicePowerCommandEvent;
import com.smartplatform.exception.AddDateException;
import com.smartplatform.exception.QueryException;
import com.smartplatform.exception.UpdateException;
import com.smartplatform.mapper.SysDeviceMapper;
import com.smartplatform.pojo.*;
import com.smartplatform.service.SysDeviceService;
import com.smartplatform.utils.RequestUtil;
import com.smartplatform.utils.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;

/**
 * @Author 咕唧
 * @Date 2022/5/30 21:38
 * @Version 1.0
 */
@Service
@Transactional
public class SysDeviceServiceImpl implements SysDeviceService {
    //从内存中获取分类数据
    private CategoriesCache categoriesCache;

    private SysDeviceMapper sysDeviceMapper;

    private SceneCache sceneCache;

    private ObjectMapper objectMapper;

    private ApplicationContext context;

    @Autowired
    public void setContext(ApplicationContext context) {
        this.context = context;
    }

    @Autowired
    public void setObjectMapper(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    @Autowired
    public void setSceneCache(SceneCache sceneCache) {
        this.sceneCache = sceneCache;
    }

    @Autowired
    public void setSysDeviceMapper(SysDeviceMapper sysDeviceMapper) {
        this.sysDeviceMapper = sysDeviceMapper;
    }

    @Autowired
    public void setCategoriesCache(CategoriesCache categoriesCache) {
        this.categoriesCache = categoriesCache;
    }

    /**
     * 添加设备
     *
     * @param deviceDto
     */
    @Override
    public void addDevice(DeviceDto deviceDto) {
        //校验数据
        Assert.isTrue(!deviceDto.isEmpty(CheckType.ADD), () -> {
            throw new QueryException("必要数据为空", ResultCode.DATA_NULL);
        });
        //对添加设备的分类进行判断，设备的分类必须存在
        Long categyId = deviceDto.getCategyId();
        SysCategory category = categoriesCache.getById(categyId);
        //分类必须存在，并且状态为正常
        Assert.isTrue(category != null && category.getStatus() == 1, () -> {
            throw new AddDateException("分类不存在", ResultCode.CATEGORY_NOT_EXIST);
        });
        //添加设备
        sysDeviceMapper.addDevice(deviceDto);
    }

    /**
     * 更新设备为出售状态
     * 只有设备处于完成生产状态才可以更新为出售状态
     * 只有设备处于出售状态时才可以绑定用户
     *
     * @param deviceId 首先将设备更新为完成生产状态
     */
    @Override
    public void updateSell(String deviceId) {
        //校验数据
        Assert.hasText(deviceId, () -> {
            throw new UpdateException("主键不符合要求", ResultCode.DEVICE_ID_ERROR);
        });
        //查询主键是否存在
        SysDevice sysDevice = sysDeviceMapper.findByDeviceId(deviceId);
        Assert.isTrue(sysDevice != null, () -> {
            throw new QueryException("设备不存在", ResultCode.CATEGORY_NOT_EXIST);
        });
        //查询设备状态是否正确
        Assert.isTrue(sysDevice.getStatus() == 0, () -> {
            throw new UpdateException("设备状态不匹配", ResultCode.DEVICE_STATUS_ERROR);
        });
        sysDeviceMapper.updateSell(deviceId);
    }

    /**
     * 设备场景和用户绑定
     *
     * @param deviceId 设备id
     * @param sceneId  场景id
     */
    @Override
    public void bindDevice(String deviceId, Long sceneId) {
        //获取当前登录的用户
        MyBaseUser sysUserInfo = SecurityUtils.getUserInfo(false);
        //如果用户没有传递场景,我们需要一个默认的场景,在我们的项目中,默认的id就是-1
        if (sceneId != -1) {
            try {
                List<SysScene> sysSceneList = sceneCache.getUserSceneCache().get(sysUserInfo.getUserId());
                long count = sysSceneList.stream()
                        .filter(sysScene -> sysScene.getSceneId().equals(sceneId))
                        .count();

                Assert.isTrue(count == 1, () -> {
                    throw new QueryException("场景不存在", ResultCode.SCENE_NOT_EXIST);
                });
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
        //判断设备是否存在，校验设备数据是否符合要求
        SysDevice sysDevice = sysDeviceMapper.findByDeviceId(deviceId);
        Assert.isTrue(sysDevice != null, () -> {
            throw new QueryException("分类不存在", ResultCode.DEVICE_NOT_EXIST);
        });
        //判断设备是否被绑定 2是被绑定
        Assert.isTrue(sysDevice.getStatus() != 2, () -> {
            throw new QueryException("设备已被绑定", ResultCode.DEVICE_ALREADY_BIND);
        });

        //判断设备状态是否可用 1是销售，3是解绑状态
        Assert.isTrue(sysDevice.getStatus() == 1 || sysDevice.getStatus() == 3, () -> {
            throw new QueryException("设备状态不可用", ResultCode.DEVICE_STATUS_NOT_MATCH);
        });
        //设置设备属性
        SysDevice device = new SysDevice();
        device.setDeviceId(deviceId);
        device.setBindUserId(sysUserInfo.getUserId());
        device.setBindTime(new Date());
        device.setSceneId(sceneId);
        sysDeviceMapper.bindDevice(device);
    }

    @Override
    public PageInfo<SysDevice> findAllDevices(int page, int limit) {
        //获取当前登录的用户
        MyBaseUser userInfo = SecurityUtils.getUserInfo(false);
        //开启分页
        PageHelper.startPage(page, limit);
        List<SysDevice> deviceList = sysDeviceMapper.findAllByUserId(userInfo.getUserId());
        //添加分类数据
        deviceList.forEach(sysDevice -> {
            //查询分类数据
            SysCategory category = categoriesCache.getById(sysDevice.getCategyId());
            sysDevice.setCategory(category);
        });
        return new PageInfo<>(deviceList);
    }

    @Override
    public void sendControl(String deviceId, String command) {
        //假设发送命令,先获取到真正要发送的数据
        //根据我们的约定,获取到对应的命令,然后发送给设备
        //根据设备的id,找到这个设备对应的分类,然后找到分类对应的命令
        Long categoryId = sysDeviceMapper.getCategoryIdByDeviceId(deviceId);
        Assert.notNull(categoryId, () -> {
            throw new QueryException("设备不存在", ResultCode.DEVICE_ID_ERROR);
        });
        //然后根据发送过来的命令关键字,找到对应的真正的命令,然后再将真正的命令发送出去
        SysCategory category = categoriesCache.getById(categoryId);
        //获取到能够发送给设备的命令
        String txCommand = category.getTxCommand();
        Assert.hasText(txCommand, () -> {
            throw new QueryException("当前设备暂时不支持该命令", ResultCode.DEVICE_COMMAND_NOT_SUPPORT);
        });

        try {
            //将命令解析成map
            Map map = objectMapper.readValue(txCommand, Map.class);
            //获取当前要发送的命令对应的真正的命令
            Map realCommand = (Map) map.get(command);
            //获取命令的类型
            Assert.notEmpty(realCommand, () -> {
                throw new QueryException("当前设备暂时不支持该命令", ResultCode.DEVICE_COMMAND_NOT_SUPPORT);
            });
            Integer type = (Integer) realCommand.get("type");
            if (type == 1) {
                //通过事件机制，将数据转发给websocket，请求由http转换为websocket
                context.publishEvent(new DevicePowerCommandEvent(deviceId, (String) realCommand.get("command")));
            } else if (type == 2) {
                //通过事件机制，将数据转发给websocket，请求由http转换为websocket
                context.publishEvent(new DeviceColorCommandEvent(deviceId, (String) realCommand.get("command")));
            }
            //更新时间
            SysDevice device = new SysDevice();
            device.setDeviceId(deviceId);
            device.setLastControlTime(new Date());
            sysDeviceMapper.updateDevice(device);

            context.publishEvent(new DeviceLogEvent(deviceId,type));
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }

    }

    @Override
    public void updateDevice(String deviceId, String ip) {
        //校验数据
        Assert.hasText(deviceId, () -> {
            throw new QueryException("当前设备id为空", ResultCode.DEVICE_ID_ERROR);
        });
        SysDevice device = new SysDevice();
        device.setDeviceId(deviceId);
        if (ip != null) {
            //更新设备状态为在线
            device.setIsOnline(1L);
            device.setConnectTime(new Date());
            device.setCurrentConnectIp(ip);
            //和设备约定，带着ip就是上线，
            String locationByIp = RequestUtil.getLocationByIp(ip);
            device.setConnectLocation(locationByIp);
        }else{
            //没有ip，表示设备离线，设置状态为离线
            device.setIsOnline(0L);
        }
        sysDeviceMapper.updateDevice(device);
    }
}
